package org.ndx.majick.ui.string;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyVetoException;
import java.lang.ref.WeakReference;

import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.PlainDocument;

import org.ndx.majick.properties.Property;
import org.ndx.majick.properties.VetoableProperty;

public class StringBackedDocument extends PlainDocument implements Document, PropertyChangeListener {
	private class PropertyUpdater implements DocumentListener {

		public void changedUpdate(DocumentEvent e) {
			update();
		}

		public void insertUpdate(DocumentEvent e) {
			update();
		}

		public void removeUpdate(DocumentEvent e) {
			update();
		}
		
		protected void update() {
			try {
				/* If source is ever reachable, we can do something, elsewhere we will have to mark the document as expired */
				if(source.get()!=null)
					source.get().set(getText(0, getLength()));
			} catch (BadLocationException e) {
				e.printStackTrace();
			}
		}
	}

	private class VetoablePropertyUpdater extends PropertyUpdater {
		protected void update() {
			try {
				((VetoableProperty<String>) source.get()).setVeto(getText(0, getLength()));
			} catch (BadLocationException e) {
				e.printStackTrace();
			} catch (PropertyVetoException e) {
				if(source.get()!=null) {
					final String text = source.get().get();
					/* Replace document text after this call */
					SwingUtilities.invokeLater(new Runnable() {
						public void run() {
							setText(text);
						}
					});
				}
			}
		}
	}

	/**
	 * Associated property. This property is stored as a weak reference in order for property to be disposed liberally
	 */
	private WeakReference<Property<String>> source;
	
	public StringBackedDocument(Property<String> source,
			StringPropertyMetadata stringMeta) {
		super();
		this.source = new WeakReference<Property<String>>(source);
		source.addPropertyChangeListener(this);
		if(source instanceof VetoableProperty) {
			addDocumentListener(new VetoablePropertyUpdater());
		} else {
			addDocumentListener(new PropertyUpdater());
		}
		setText(source.get());
	}
	
	/**
	 * When source changes, updates document accordingly, by replacing all document content.
	 */
	public void propertyChange(PropertyChangeEvent evt) {
		String text = (String) evt.getNewValue();
		setText(text);
	}

	/**
	 * Sets document text according to input text
	 * @param text input text
	 */
	private void setText(final String text) {
		if(!SwingUtilities.isEventDispatchThread()) {
			try {
				SwingUtilities.invokeAndWait(new Runnable() {
					public void run() {
						setText(text);
					}
				});
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		try {
			if(!getText(0, getLength()).equals(text)) {
				replace(0, getLength(), text, null);
			}
		} catch (BadLocationException e) {
			e.printStackTrace();
		}
	}

}
